import numpy as np

'''
Sigmoid 激活函数
'''
class Sigmoid:
    def __init__(self):
        self.params, self.grads = [],[]
        self.out = None
    def forward(self, x):
        out = 1 / (1 + np.exp(-x))
        self.out = out
        return out
    
    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out
        return dx
    
'''
Affine 层
注意参数W表示权重，b表示偏置
W 表示权重矩阵（这个矩阵的形式和x的表示形式的选择是有关），b 表示偏置向量
'''
class Affine:
    def __init__(self, W, b):
        self.params = [W, b]
        self.grads = [np.zeros_like(W), np.zeros_like(b)]
        self.x = None
    def forward(self, x):
        W, b = self.params
        out = np.dot(x, W) + b
        self.x = x
        return out
    
    def backward(self, dout):
        W, b = self.params
        dx = np.dot(dout, W.T)
        dW = np.dot(self.x.T, dout)
        # 这里省略号的用法值得注意：暂时理解为深复制（deep copy）
        db = np.sum(dout, axis=0)
        self.grads[0][...] = dW
        self.grads[1][...] = db
        return dx
    
class MatMul:
    def __init__(self, W):
        self.params = [W]
        self.grads = [np.zeros_like(W)]
        self.x = None

    def forward(self, x):
        W,   = self.params
        out = np.dot(x, W)
        self.x = x
        return out
    
    def backward(self, dout):
        W,   = self.params
        dx = np.dot(dout, W.T)
        dW = np.dot(self.x.T, dout)
        # 这里省略号的用法值得注意：暂时理解为深复制（deep copy）
        self.grads[0][...] = dW
        return dx
    
class SoftmaxWithLoss:
    def __init__(self):
        self.params, self.grads = [], []
        self.y = None # softmax的输出
        self.t = None # 监督数据
    
    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        
        # 监督数据为one-hot-vector的情况
        if self.t.size == self.y.size:
           self.t = self.t.argmax(axis=1)


'''
阶跃函数
'''
def step_function(x):
    return np.array(x > 0, dtype=np.int)

'''
ReLU 激活函数
'''
def relu(x):
    return np.maximum(0, x)

'''
softmax函数: 这个函数的值位于0到1之间，而且和为1，通常把它解释为“概率”
这里有一个技巧需要注意：指数运算的结果有时会比较大，容易溢出，这个时候稍微做一个变形
1. 先减去最大值，防止溢出
2. 求指数
3. 求和
原理在于如下恒等变换：
    exp(a) / sum(exp(a)) = exp(a - c) / sum(exp(a - c))
'''
def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a - c) # 防止溢出
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y


